home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / DNet / DGoNetDoc.cpp < prev    next >
Text File  |  1996-07-05  |  55KB  |  2,113 lines

  1. // DGopherDoc.cp
  2. // d.g.gilbert
  3.  
  4.  
  5.  
  6. #include "DGoNetDoc.h"
  7.  
  8. #include <DGoDoc.h>
  9.  
  10. #include <ncbi.h>
  11. #include <dgg.h>
  12. #include <Dvibrant.h>
  13. #include <DApplication.h>
  14. #include <DGopher.h>
  15. #include <DGoList.h>
  16. #include <DGoInit.h>
  17. #include <DGoPlus.h>
  18. #include <DGoClasses.h>
  19. #include <DPanel.h>
  20. #include <DControl.h>
  21. #include <DDialogText.h>
  22. #include <DWindow.h>
  23. #include <DMenu.h>
  24. #include <DFile.h>
  25. #include <DUtil.h>
  26. #include <DIconLib.h>  
  27. #include <DIcons.h> 
  28.  
  29. #include <DChildApp.h>
  30.  
  31. #include <DRichViewNu.h>
  32. #include <DRTFHandler.h>
  33. #include <DHTMLHandler.h>
  34. #include <DPICTHandler.h>
  35. #include <DGIFHandler.h>
  36.  
  37. // DAMN SUN CC/LD can't find DNetHTML objects !
  38. #include <DNetHTMLHandler.h>
  39.  
  40.  
  41. enum ResizeWindSizes {
  42.     kWinDragHeight = 20,
  43.     kWinScrollWidth = 0,   // added to both vert & hor.
  44.     kWinMinWidth = 0,
  45.     kWinExtraHeight= 0
  46.     };
  47.  
  48. /* from vibrant/vibwndws.c */
  49. extern "C" void Nlm_ResizeWindow(Nlm_GraphiC w, Nlm_Int2 dragHeight,
  50.                               Nlm_Int2 scrollWidth, Nlm_Int2 minWidth,
  51.                               Nlm_Int2 extraHeight);
  52. /* for doc window: Nlm_ResizeWindow (w, 20, 0, Nlm_StringWidth (title) + 70, 0); */
  53.  
  54.  
  55. //Global     DList*    gTextStoreList = NULL;
  56. //Local        short        gListAt = kEmptyIndex;
  57. Global  DGopherTextDoc* gGopherTextDoc = NULL;
  58.  
  59. Local unsigned long gLinkcolor = (0<<16) + (170<<8) + 0;
  60.                                                               // red      green    blue
  61.  
  62.  
  63. enum { kGolistViewWidth = 225 };
  64.  
  65. enum DGoDocCmds {
  66.     cProcessGopher = 30346,
  67.     cStuf,
  68.     cStufInline,
  69.     cSave,
  70.     cAdd,
  71.     cProcessInlines
  72.     };
  73.  
  74. Local Nlm_FonT    gSmallFont= NULL;
  75.  
  76.  
  77. inline void MakeSmallFont()
  78. {
  79.     if (!gSmallFont) 
  80.         gSmallFont = Nlm_GetFont( "Times", 9, false, false, false, "Roman");
  81. }
  82.  
  83.  
  84.  
  85.  
  86. class DLinkedRichView : public DRichView
  87. {
  88. public:
  89.     enum MarkState {
  90.         kMarkNone, kMarkMoving, kMarkOff, kMarkOn
  91.         };
  92.     Nlm_PoinT    fAnchorPt, fEndPt;
  93.     MarkState fMarqueeState;
  94.     DGoLinkedTextDoc * fDoc;
  95.     
  96.     DLinkedRichView(long id, DGoLinkedTextDoc* itsSuperior, short pixwidth, short pixheight);
  97.  
  98.     virtual void DoClickFetch(Nlm_PoinT mouse);
  99.     virtual void Click(Nlm_PoinT mouse);
  100.     virtual void Drag(Nlm_PoinT mouse);
  101.     virtual void Hold(Nlm_PoinT mouse);
  102.     virtual void Release(Nlm_PoinT mouse);
  103.     virtual void DrawMarquee( MarkState state = kMarkMoving);
  104. };
  105.  
  106.  
  107. DLinkedRichView::DLinkedRichView(long id, DGoLinkedTextDoc* itsSuperior, short pixwidth, short pixheight):
  108.     DRichView( id, itsSuperior, pixwidth, pixheight),
  109.     fDoc( itsSuperior),
  110.     fMarqueeState(kMarkNone)
  111. {
  112.     Nlm_LoadPt( &fAnchorPt, 0, 0);
  113.     fEndPt= fAnchorPt;
  114. }
  115.  
  116.  
  117.  
  118.  
  119. void DLinkedRichView::Click(Nlm_PoinT mouse)
  120. {
  121.     if (fMarqueeState == kMarkOn) DrawMarquee( kMarkOff);
  122.     if (Nlm_dblClick) {
  123.         if (gDoubleClicker) DoClickFetch( mouse);
  124.         //else DoubleClick(mouse);
  125.         }
  126.     else {
  127.         fAnchorPt= mouse;
  128.         fEndPt= mouse;
  129.         if (gSingleClicker) DoClickFetch( mouse);
  130.         else SingleClick(mouse);        
  131.         }
  132. }
  133.  
  134.  
  135. void DLinkedRichView::DrawMarquee( MarkState state)
  136. {
  137.     if (state != kMarkNone && ! Nlm_EqualPt(fAnchorPt, fEndPt)) {
  138.         Nlm_RecT r;
  139.         r.left= MIN(fAnchorPt.x, fEndPt.x);
  140.         r.right= MAX(fAnchorPt.x, fEndPt.x);
  141.         r.top= MIN(fAnchorPt.y, fEndPt.y);
  142.         r.bottom= MAX(fAnchorPt.y, fEndPt.y);
  143.         switch (state) {
  144.             case kMarkMoving:
  145.                 Nlm_InvertMode();
  146.                 Nlm_Dotted();
  147.                 break;
  148.             case kMarkOff:
  149.                 Nlm_InvertMode();
  150.                 break;
  151.             case kMarkOn:
  152.                 Nlm_InvertMode();
  153.                 break;
  154.             }
  155.         Nlm_FrameRect( &r);
  156.         switch (state) {
  157.             case kMarkMoving:
  158.                 Nlm_Solid();
  159.                 Nlm_CopyMode();
  160.                 fMarqueeState= kMarkMoving;
  161.                 break;
  162.             case kMarkOff:
  163.                 Nlm_CopyMode();
  164.                 fMarqueeState= kMarkNone;
  165.                 break;
  166.             case kMarkOn:
  167.                 Nlm_CopyMode();
  168.                 fMarqueeState= kMarkOn;
  169.                  break;
  170.             }
  171.         }
  172. }
  173.  
  174.  
  175. void DLinkedRichView::Hold(Nlm_PoinT mouse)
  176. {
  177.         // for marquee selection ??
  178.     //DrawMarquee(kMarkMoving);
  179.     //fEndPt= mouse;
  180.     //DrawMarquee(kMarkMoving);
  181. }
  182.  
  183. void DLinkedRichView::Drag(Nlm_PoinT mouse)
  184. {
  185.         // for marquee selection
  186.     DrawMarquee(kMarkMoving);
  187.     fEndPt= mouse;
  188.         // !! need to scroll doc if mouse is outside of doc view, but in doc area
  189.     DrawMarquee(kMarkMoving);
  190. }
  191.  
  192.  
  193. void DLinkedRichView::Release(Nlm_PoinT mouse)
  194. {
  195.     if (fMarqueeState != kMarkNone) {
  196.             // don't mark things if this was just a simple, non-dragged click
  197.             // for marquee selection
  198.         DrawMarquee(kMarkMoving);
  199.         fEndPt= mouse;
  200. #if 1
  201.         Nlm_RecT r;
  202.         r.left= MIN(fAnchorPt.x, fEndPt.x);
  203.         r.right= MAX(fAnchorPt.x, fEndPt.x);
  204.         r.top= MIN(fAnchorPt.y, fEndPt.y);
  205.         r.bottom= MAX(fAnchorPt.y, fEndPt.y);
  206.     
  207.         // ClearSelection();
  208.         if (r.right > r.left + 5 || r.bottom > r.top + 5)
  209.             MarkSelection( r);
  210. #else
  211.         DrawMarquee(kMarkOn);
  212. #endif
  213.     }
  214. }
  215.  
  216.  
  217.  
  218. void DLinkedRichView::DoClickFetch(Nlm_PoinT mouse)
  219. {
  220.     short item, row;
  221.     Nlm_RecT        rct;
  222.     char                wordAt[512];
  223.     DRichStyle    styleAt;
  224.  
  225.     wordAt[0]= 0;
  226.     MapPoint( mouse, item, row, &rct, wordAt, 512, &styleAt);
  227.     if (styleAt.ispict && styleAt.fPict) {
  228.         Nlm_PoinT atp;
  229.         short    linkid, kind = 0;
  230.         atp= mouse;
  231.         MapView2Doc( atp);
  232.         // if atp is inside a pict, adjust it by pict top,left
  233.         atp.x -= fDrawr.left;
  234.         atp.y    -= fDrawr.top;
  235.  
  236.         linkid= styleAt.fPict->GetLink( atp, kind); 
  237.                 
  238.         if (kind == DPictStyle::kPictNetPic)
  239.             fDoc->SetupInlinePart( fMapStyle); 
  240.         else if (linkid) {
  241.             // if styleAt.ismap, do things w/ gopher link
  242.             // need to stuff atp.x,y into gopher.fQuery....
  243.             if (styleAt.ismap) fDoc->OpenGopherLink(linkid-1, &atp);    
  244.             else fDoc->OpenGopherLink(linkid-1, NULL);    // send atp anyway ???
  245.             }
  246.         else if (fDoc->fStore->fMapLink) {
  247.             fDoc->OpenGopherLink( fDoc->fStore->fMapLink-1, &atp);    
  248.             }
  249.         else {
  250.             fDoc->SetupInlinePart( fMapStyle); // ?? check in case it needs pic fetched
  251.             }
  252.         }
  253.     else {
  254.         if (styleAt.linkid) fDoc->OpenGopherLink(styleAt.linkid-1);    
  255.         }
  256.     styleAt.ownpict= false; // !!!! not this copy, so we don't delete
  257. }
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274. // class    DGopherTextStore : public DTaskMaster
  275.  
  276.  
  277.  
  278. DGopherTextStore::DGopherTextStore( DGopher* itsGopher, DWindow* itsWindow, Boolean havelinks) :
  279.         DTaskMaster( 0, gApplication),
  280.         fWindow(itsWindow), fIsLinkDoc(havelinks),
  281.         fParentGo(itsGopher), fStufGo(NULL),
  282.         fRichView(NULL),  
  283.         fDocFormat(DRichHandler::kTextformat),
  284.         fRichHandler(NULL), 
  285.         fInlineStyle(NULL),
  286.         fDocFile(NULL), fFetchFile(NULL),
  287.         fGolist(NULL), fIsFromFile(false), fMapLink(0),
  288.         fFirststuff(1)
  289.     short pixwidth = gPrintManager->PageWidth(), pixheight = 0;
  290.     
  291.     if (fParentGo) {
  292.         fParentGo->newOwner();
  293.         if (fParentGo->fType == kTypeHtml) fDocFormat= DHTMLHandler::kHTMLformat;
  294.         }
  295.     fFetchFile= new DTempFile();
  296.     //fFetchFile->Open("wb"); //! << WITHOUT THIS, save IS BAD for Binary data
  297.  
  298. #if 1    
  299.     if (((DGopherTextDoc*)fWindow)->fTextStoreList->GetSize()) {
  300.             // already have a view in window, use same size
  301.         Nlm_RecT r;
  302.         ((DGopherTextDoc*)fWindow)->fRichView->GetPosition( r);
  303.         pixwidth=  r.right - r.left;
  304.         pixheight= r.bottom- r.top;
  305.                 // need this for all systems
  306.         pixwidth -= 16; // Damn, this is scrollbar width, why do we need this adjustment !?
  307.         }
  308.     else if (!Nlm_EmptyRect( &DRichTextDoc::gRichDocRect)) {
  309.         Nlm_PoinT  npt;
  310.         //fWindow->GetNextPosition( &npt);
  311.         pixwidth= DRichTextDoc::gRichDocRect.right - DRichTextDoc::gRichDocRect.left
  312.                 + 20; //- npt.x;
  313.         pixheight= DRichTextDoc::gRichDocRect.bottom - DRichTextDoc::gRichDocRect.top
  314.                 + 8; //- npt.y;
  315.         }
  316. #endif        
  317.     if (fIsLinkDoc)
  318.         fRichView= new DLinkedRichView(0, (DGoLinkedTextDoc*)fWindow, pixwidth, pixheight);
  319.     else
  320.       fRichView= new DRichView(0, fWindow, pixwidth, pixheight);
  321.     fRichView->SetSlateBorder( true);
  322.     fRichView->SetResize( DView::matchsuper, DView::relsuper);
  323.     fRichView->SetTabs( gTextTabStops);
  324.     //fRichView->SizeToSuperview( fWindow, true, false); // this needs to adjust for if view has scrollbar !
  325. }
  326.         
  327.  
  328. DGopherTextStore::~DGopherTextStore()
  329. {
  330.     this->Close();
  331.     if (fGolist) fGolist->suicide(); //!
  332.     if (fParentGo) fParentGo->suicide();  
  333.   if (fRichView) fRichView->ResetDoc(); // drop all data, but leave view for deleteSubviews
  334.     // ?? ^^ ResetDoc is a memory sieve !!???!  doesn't free any of the view mem ?? or is bug elsewhere
  335.     if (fRichHandler) delete fRichHandler; fRichHandler= NULL;
  336.     if (fDocFile) delete fDocFile; fDocFile= NULL;
  337.     if (fFetchFile) delete fFetchFile; fFetchFile= NULL;
  338. }
  339.  
  340. Boolean DGopherTextStore::suicide(void) 
  341.     if (GetOwnerCount() <= 1) { 
  342.         delete this; 
  343.         return true; 
  344.         }
  345.     else 
  346.         return DObject::suicide();
  347. }
  348.  
  349. Boolean DGopherTextStore::suicide(short ownercount)
  350. {
  351.     if (ownercount < 1) {
  352.         delete this; 
  353.         return true; 
  354.         }
  355.     else 
  356.         return DObject::suicide(ownercount);
  357. }
  358.  
  359. #if 0
  360. void DGopherTextStore::AddToList()
  361. {
  362.     if (!gTextStoreList) {
  363.         gTextStoreList= new DList(NULL, DList::kDeleteObjects);
  364.         gListAt= kEmptyIndex;
  365.         }
  366.     if (gTextStoreList) {
  367.             // clean out storage below current displayed item...
  368.             // ???? is this going to be a mess w/ different windows ??
  369.       for (short idoc= gTextStoreList->GetSize()-1; idoc>gListAt; idoc--)
  370.           gTextStoreList->AtDelete(idoc); // deletes all of storage
  371.         gTextStoreList->Push( this);
  372.         gListAt= gTextStoreList->GetSize()-1;
  373.         }
  374. }
  375. #endif
  376.  
  377. void DGopherTextStore::SetGolist( DGopherList *theList)
  378. {
  379.     fGolist= theList;
  380.     if (fParentGo && fParentGo->fGoMenuBlock) {
  381.         fGolist= fParentGo->fGoMenuBlock->fGolist;
  382.         if (fGolist) fGolist->newOwner();
  383.         }
  384.         
  385.     if (fParentGo && fParentGo->fIsLocal) {
  386.             // look for a separate "path.go4" link file
  387.         DFile* goFile= NULL;
  388.         char    name[512];
  389.         StrNCpy( name, fParentGo->GetPath(), 512);
  390.         gFileManager->ReplaceSuffix( name, 512, DGopherListDoc::kGopherDocSuffix);
  391.         if (gFileManager->FileExists( name))  
  392.             goFile= new DFile(name, "r");
  393.         if (goFile) {
  394.             ulong filelen;
  395.             goFile->OpenFile();
  396.             goFile->GetDataLength(filelen);
  397.             char* buf= (char*) MemNew( filelen+1);
  398.             goFile->ReadData( buf, filelen);
  399.             fGolist->ReadLinks( buf, filelen);
  400.             MemFree( buf);
  401.             goFile->CloseFile();
  402.             delete goFile;
  403.             }
  404.         }
  405.         
  406.     if (fGolist) fGolist->fParentMenu= fParentGo;
  407. }
  408.  
  409. void DGopherTextStore::WriteDoc( DFile* toFile, char* readkind, char* writekind)
  410. {
  411.     enum { kLineMax = 2048 };
  412.     char     line[kLineMax];
  413.     ulong count, bytesleft;
  414.     
  415.     if (!fDocFile) {
  416.         if (!fFetchFile) return;
  417.         fDocFile= fFetchFile;
  418.         fFetchFile= new DTempFile();
  419.         }
  420.     fDocFile->Open(readkind);
  421.     fDocFile->GetDataLength(bytesleft);
  422.     toFile->Open(writekind);
  423.     while (bytesleft>0) {
  424.         count= MIN(kLineMax, bytesleft);
  425.         fDocFile->ReadData(line, count);
  426.         bytesleft -= count;
  427.         toFile->WriteData(line, count);
  428.         }
  429.     toFile->Close();
  430.     fDocFile->Close();
  431. }
  432.  
  433.  
  434. const char* DGopherTextStore::GetTitle()
  435. {
  436.     static char buf[256];
  437.     if (fParentGo) return fParentGo->ShortName();
  438.     else if (fStufGo) return fStufGo->ShortName();
  439.     else if (fRichView) return fRichView->GetTitle( buf, 256); 
  440.     else return "";
  441. }
  442.  
  443. void DGopherTextStore::StartInlineFetch( DGopher* stufGo, DRichStyle* stufStyle)
  444. {
  445.     fStufGo= stufGo;
  446.     fInlineStyle= stufStyle;
  447.     fFirststuff= 1;
  448.     if (!fDocFile && fFetchFile) {
  449.         fDocFile= fFetchFile;
  450.         fFetchFile= new DTempFile();
  451.         }
  452. }
  453.  
  454. Boolean DGopherTextStore::IsMyTask(DTask* theTask) 
  455. {
  456.     if (theTask->fKind == DGopherTextDoc::kGoTextdoc) {
  457.         ProcessTask( theTask);
  458.         return true;
  459.         }
  460.     else 
  461.         return DTaskMaster::IsMyTask(theTask);
  462. }
  463.  
  464.  
  465. ulong CRLF2Newline( char* databuf, ulong datasize)
  466. {
  467.     ulong  oldsize = 0;
  468.     char lastc = 0, * newp = databuf, * oldp = databuf;
  469.     
  470.     while (oldsize < datasize) {
  471.         if (*oldp == kLF && lastc == kCR) {
  472.             newp[-1]= '\n'; // system newline
  473.             lastc= kLF;
  474.             oldp++;
  475.             oldsize++;
  476.             }
  477.         else {
  478.             *newp++= lastc= *oldp++;
  479.             oldsize++;
  480.             }
  481.         }
  482.     *newp= 0;
  483.     return newp - databuf;
  484.  
  485. char* DGopherTextStore::ProcessTask1( char* databuf, ulong& datasize) 
  486. {
  487.     char *dataAt;
  488.  
  489.     fFirststuff++;    
  490.     
  491.     if (!fFetchFile) fFetchFile= new DTempFile();
  492.     fFetchFile->Open("wb");  
  493.     fRichHandler = new DRTFHandler(fRichView, fFetchFile);
  494.     dataAt= fRichHandler->IsRich( databuf, datasize);
  495.     if (dataAt) {
  496.         fDocFormat= fRichHandler->Format();
  497.         return dataAt;
  498.         }
  499.     delete fRichHandler;
  500.  
  501.     fRichHandler = new DPICTHandler(fRichView, fFetchFile);
  502.     dataAt= fRichHandler->IsRich( databuf, datasize);
  503.     if (dataAt) {
  504.         fDocFormat= fRichHandler->Format();
  505.         fStufGo->fTransferType= kTransferBinary;  // ! this mayn't be safe ?!
  506.         return dataAt;
  507.         }
  508.     delete fRichHandler;
  509.  
  510.     fRichHandler = new DGIFHandler(fRichView, fFetchFile);
  511.     dataAt= fRichHandler->IsRich( databuf, datasize);
  512.     if (dataAt) {
  513.         fDocFormat= fRichHandler->Format();
  514.         fStufGo->fTransferType= kTransferBinary;  // ! this mayn't be safe ?!
  515.         return dataAt;
  516.         }
  517.     delete fRichHandler;
  518.                 
  519.     fRichHandler = new DNetHTMLHandler(fRichView, fFetchFile, fGolist);
  520. #if 0
  521.     // ?? not always ??
  522.     if (fStufGo && fStufGo->fProtocol == DGopher::kHTTPprot)
  523.         dataAt= databuf;
  524.     else
  525. #endif
  526.         dataAt= fRichHandler->IsRich( databuf, datasize);
  527.     if (dataAt) {
  528.         fDocFormat= fRichHandler->Format();
  529.         return dataAt;
  530.         }
  531.     delete fRichHandler;
  532.  
  533.     fRichHandler = new DRichHandler(fRichView, fFetchFile);
  534.     fDocFormat= fRichHandler->Format();
  535.     fStufGo->fTransferType= kTransferText; // fix problems caused by binary test
  536.     datasize= CRLF2Newline( databuf, datasize); 
  537.     fStufGo->fInfoSize= datasize;    // ??    
  538.     return databuf; 
  539. }
  540.  
  541.  
  542. void DGopherTextStore::ProcessTask(DTask* theTask) 
  543. {
  544.     if (theTask->fNumber == cStuf || theTask->fNumber == cStufInline) {
  545.  
  546.          Boolean gotInline= (theTask->fNumber == cStufInline);
  547.  
  548.         if ( fStufGo 
  549. #if 0
  550.             && (fStufGo->fInfoSize || fStufGo->ThreadProgress() != DGopher::kThreadDoneReading) 
  551. #endif
  552.             ) {
  553.             char * cbeg, * cend, clineend;
  554.             Boolean update= true, endOfData;
  555.             short lastItemCount=0, lastLineCount;
  556.             ulong dataRemaining, oldsize;
  557.             
  558.             endOfData= fStufGo->ThreadProgress() == DGopher::kThreadDoneReading;
  559.             fStufGo->ShowProgress();            
  560.             cbeg = fStufGo->fInfo; 
  561.             ulong csize= fStufGo->fInfoSize;
  562.             if (cbeg==NULL || csize==0) {
  563.                 if (! (endOfData && fRichHandler) ) return; 
  564.                 }
  565.             cend = cbeg + csize; // - 1; 
  566.             clineend = *LineEnd; 
  567.             
  568.             if (!fRichHandler || fFirststuff == 1) {
  569.                 oldsize= csize;
  570.                 char* cp= ProcessTask1(cbeg, csize);
  571.                 fRichHandler->fTasknum= theTask->fNumber - cStuf;
  572.                 if (!cp) return; 
  573.                 else if (cp > cbeg) {
  574.                     if (cp > cend) csize= 0; 
  575.                     else csize= cend - cp; // + 1; 
  576.                     Nlm_MemMove( cbeg, cp, csize);
  577.                     cend= cbeg + csize; // - 1;
  578.                     fStufGo->fInfoSize= csize;        
  579.                     }
  580.                 else if (oldsize != csize) {
  581.                     fStufGo->fInfoSize= csize;    
  582.                     cend = cbeg + csize; // - 1; 
  583.                     }
  584.                 }
  585.                 
  586.             if (fFirststuff>0)  
  587.                 fRichView->GetDocStats( lastItemCount, lastLineCount);
  588.              
  589.             dataRemaining = fStufGo->fInfoSize;
  590.             update= fRichHandler->ProcessData( cbeg, cend, endOfData, dataRemaining);
  591.             if (update) fStufGo->fInfoSize= dataRemaining;        
  592.             
  593.             if (endOfData && gotInline) {
  594.                 fRichHandler->InstallInStyle( fRichView, fInlineStyle);
  595.                 fInlineStyle= NULL;
  596.                 }
  597.  
  598.             if (update && fFirststuff>0 && update != 3 && !gotInline) { 
  599.                 Nlm_RecT  invalr;
  600.                 short i, winheight, pixh, pixold, 
  601.                             itemCount, lineCount, lineHeight, startsAt, rowCount;
  602.  
  603.                 invalr= fRichView->fViewrect;
  604.                 // check if we have updated all of display...
  605.                 winheight= invalr.bottom  -  invalr.top;
  606.                 fRichView->GetDocStats( itemCount, lineCount);
  607.                 pixold= 0;
  608.                 for (i= 1, pixh= 0; i<=itemCount; i++) {
  609.                     fRichView->GetItemStats( i, startsAt, rowCount, lineHeight);
  610.                     pixh += rowCount * lineHeight;
  611.                     if (i<=lastItemCount) pixold= pixh;
  612.                     }
  613.                 // remember pixh from last time & only inval new part !?
  614.                 invalr.top += pixold;
  615.                 fRichView->InvalRect(invalr);
  616.                 if (pixh >= winheight) fFirststuff= 0; 
  617.                 }
  618.  
  619.             }
  620.         
  621.         if (!fStufGo || fStufGo->ThreadProgress() == DGopher::kThreadDoneReading) {
  622.             theTask->SetRepeat(false); // done w/ gopher fetch, dequeue this task
  623.             //this->ShowMessage("done reading");
  624.             fFetchFile->Close();
  625.             delete fRichHandler; 
  626.             fRichHandler= NULL;
  627.             }
  628.         }
  629. }
  630.  
  631.  
  632. void DGopherTextStore::Close()
  633. {
  634.     if (fStufGo && fStufGo->ThreadProgress() != DGopher::kThreadDoneReading) {
  635.             // kill tcp connection !!!!!!!!
  636.         fStufGo->CloseQuery();
  637.         fStufGo->fThreadState= DGopher::kThreadNotStarted;  
  638.         fStufGo= NULL; // don't delete !?
  639.         }
  640.     if (fFetchFile) fFetchFile->Close();
  641.     if (fDocFile) fDocFile->Close();
  642. }
  643.  
  644.  
  645.  
  646. Boolean DGopherTextStore::OpenStore()
  647. {
  648.     // ?! need to link this proc w/ Window.Open() procs
  649.  
  650. #if 0
  651.             // do this in constructor !?
  652.     if (!fRichView) {
  653.         if (fIsLinkDoc)
  654.             fRichView= new DLinkedRichView(0, (DGoLinkedTextDoc*)fWindow, 
  655.                                                         gPrintManager->PageWidth(), 0);
  656.         else
  657.           fRichView= new DRichView(0, fWindow, gPrintManager->PageWidth(), 0);
  658.         }
  659.     fRichView->SetSlateBorder( true);
  660.     fRichView->SetResize( DView::matchsuper, DView::relsuper);
  661.     fRichView->SetTabs( gTextTabStops);
  662.     //fRichView->SizeToSuperview( fWindow, true, false); // this needs to adjust for if view has scrollbar !
  663. #endif
  664.         
  665.   if (!fGolist) fGolist= new DGopherList(NULL);
  666.     fGolist->fParentMenu= fParentGo;  
  667.     if (!fIsFromFile && fParentGo) {
  668.         fGolist->InsertFirst( fParentGo); // !?!? put papa into list, 
  669.                 // mainly for use  with extract !?
  670.         if (fParentGo->fThreadState == DGopher::kThreadNotStarted) 
  671.             fParentGo->ReadTask();  // tell gopher to read data thru a repeat task
  672.         if (fParentGo->fThreadState == DGopher::kThreadDoneReading 
  673.           && fParentGo->fInfoSize < 1) { 
  674.             fParentGo->fThreadState= DGopher::kThreadNotStarted; // ! need to reset this somewhere for used gophers
  675.             this->suicide(); 
  676.             return false;  // ?? return false; ?? 
  677.             }
  678.         fStufGo= fParentGo;
  679.         //DTask* aStuffTask= newTask(cStuf, DGopherTextDoc::kGoTextdoc);
  680.         DTask* aStuffTask= new DTask(cStuf, DGopherTextDoc::kGoTextdoc, fWindow);
  681.         aStuffTask->SetRepeat(true);
  682.         this->PostTask( aStuffTask);
  683.         }
  684.         
  685.     return true; 
  686. }
  687.     
  688.  
  689.  
  690.  
  691.  
  692.  
  693.  
  694.  
  695.  
  696.  
  697.  
  698.  
  699.  
  700.  
  701.  
  702.  
  703.  
  704.  
  705.  
  706. // class    DGopherTextDoc : public DRichTextDoc
  707.  
  708.  
  709. DGopherTextDoc::DGopherTextDoc(long id, DGopher* itsGopher, Boolean havelinks) :
  710.         DRichTextDoc( id, false),
  711.         fInUse(false), fPinned(false), fStickpin(NULL),
  712.         fTextStoreList(NULL), fListAt(kEmptyIndex),
  713.         fStore(NULL), fStatus(NULL)
  714.     DIconButton* ib;
  715.     
  716.     if (fRichView) { this->RemoveSubview( fRichView, true); fRichView= NULL; }
  717.     
  718.     //fTextStoreList= new DList(NULL, DList::kDeleteObjects);
  719.     fTextStoreList= new DList(NULL);
  720.     fStickpin= new DIconButton( cPushpin, this, &gPushpinOut);
  721.     this->NextSubviewToRight();
  722. #if 0
  723.     ib = new DIconButton( cParentDrop, this, &gDownTriangle);
  724.     this->NextSubviewBelowLeft();
  725. #endif
  726.     ib = new DIconButton( cShiftBack, this, &gLeftTriangle);
  727.     this->NextSubviewToRight();
  728.     fShiftBack= ib; fShiftBack->Disable();
  729.     ib = new DIconButton( cShiftFore, this, &gRightTriangle);
  730.     this->NextSubviewBelowLeft();
  731.     fShiftFore= ib; fShiftFore->Disable();
  732.     
  733.     this->GetNextPosition( &fTextViewLoc);
  734.     fTextViewLoc.x= 0; // fix for odd offset
  735.     this->SetNextPosition( fTextViewLoc);
  736.     fStore= new DGopherTextStore( itsGopher, this, havelinks);
  737.     fRichView= fStore->fRichView;
  738.     
  739.     AddToList(fStore);
  740.     fSaveHandler= this;  
  741.     fPrintHandler= fRichView; // make sure current view is printed
  742.     this->NextSubviewBelowLeft();
  743. }
  744.         
  745. DGopherTextDoc::~DGopherTextDoc()
  746. {
  747.     if (fTextStoreList) {
  748.             // !! DAMMIT, delete fTextStoreList IS NOT freeing fStore objects !!
  749.         short i, n= fTextStoreList->GetSize();
  750.         for (i=0; i<n; i++) {
  751.             DGopherTextStore* st= (DGopherTextStore*) fTextStoreList->At(i);
  752.             delete st; // !!!!! DIE DAMMIT
  753.             }
  754.         delete fTextStoreList; // deletes all objects in list
  755.         }
  756.     //if (fStore) delete fStore; //<< fTextStoreList kills this
  757. }
  758.  
  759.  
  760. void DGopherTextDoc::AddToList( DGopherTextStore* theStore)
  761. {
  762.         // clean out storage below current displayed item...
  763.   for (short idoc= fTextStoreList->GetSize()-1; idoc>fListAt; idoc--) {
  764.         DGopherTextStore* st= (DGopherTextStore*) fTextStoreList->At(idoc);
  765.         delete st; // !!!!! DIE DAMMIT
  766.       fTextStoreList->AtDelete(idoc); // DOESN"T delete all of storage
  767.       }
  768.     fTextStoreList->Push( theStore);
  769.     fListAt= fTextStoreList->GetSize()-1;
  770. }
  771.  
  772.  
  773. Boolean DGopherTextDoc::PushNewDoc( DGopher* itsGopher, DGopherList* itsList, Boolean havelinks)
  774. {
  775.     // !! need to test for free mem here, free top of fTextStoreList if full...
  776.     if (!fInUse && !fPinned) {
  777.         this->SetNextPosition( fTextViewLoc);
  778.         DGopherTextStore* newStore= new DGopherTextStore( itsGopher, this, havelinks);
  779.         if (!newStore->OpenStore()) { 
  780.             delete newStore;
  781.             }
  782.         else {
  783.           fRichView->Hide();
  784.             fStore= newStore;
  785.             fRichView= fStore->fRichView;
  786.             this->AddToList(fStore);
  787.             if (fStatus) {
  788.                 fStore->fGolist->SetStatus( fStatus);
  789.                 if (fStore->fParentGo) fStore->fParentGo->fStatusLine= fStatus;
  790.                 }
  791.             this->SetTitle((char*)fStore->GetTitle());
  792.             fSaveHandler= this; // replace fRichView's handler w/ us        
  793.             fPrintHandler= fRichView; // make sure current view is printed
  794.             this->ShowMessage("Reading...");
  795.             gGopherTextDoc= this; // track front gopher doc/window
  796.             fRichView->SizeToSuperview( this, true, false); // this needs to adjust for if view has scrollbar !
  797.             this->Invalidate();
  798.             fShiftBack->Enable();
  799.             fShiftFore->Disable();
  800.             //this->Select();    //?? DWindow::Open()
  801.             //this->Show();     //?? DWindow::Open()
  802.             }
  803.         return true;
  804.         }
  805.     else
  806.         return false;
  807. }
  808.  
  809.  
  810. Boolean DGopherTextDoc::PopDoc( short numitems)
  811. {
  812.     DGopherTextStore* astore = NULL;
  813.     if (fTextStoreList->GetSize() != kEmptySize) {
  814.         short newat = fListAt - numitems;
  815.         if (newat > kEmptyIndex && newat < fTextStoreList->GetSize()) {
  816.             fListAt= newat;
  817.             astore= (DGopherTextStore*) fTextStoreList->At( fListAt);
  818.             }
  819.         }
  820.     if (astore) {
  821.       fRichView->Hide();
  822.         fStore= astore;
  823.         fRichView= fStore->fRichView;
  824.             // ?? need to make sure fRichView is in this window  !! YES
  825.         //fRichView->SetWindow(this); //<< doesn't set scrollbars, other stuff !?
  826.         this->SetTitle( (char*)fStore->GetTitle());
  827.       fRichView->Select(); //?? for motif
  828.       fRichView->Show();
  829.       
  830.         if (fListAt+1 < fTextStoreList->GetSize()) fShiftFore->Enable();
  831.         else fShiftFore->Disable();
  832.         if (fListAt > 0) fShiftBack->Enable();
  833.         else fShiftBack->Disable();
  834.         return true;
  835.         }
  836.     else
  837.         return false;
  838. }
  839.  
  840.  
  841. void DGopherTextDoc::Close()
  842. {
  843.     fStore->Close();
  844.     if (gGopherTextDoc == this) gGopherTextDoc= NULL; // track front gopher doc/window
  845.     fInUse= false;
  846.     fPinned= false;
  847.     DRichTextDoc::Close();
  848. }
  849.  
  850.  
  851. void DGopherTextDoc::Activate()
  852. {    
  853.     if (DGopherListDoc::gLockWinMenuItem) 
  854.         DGopherListDoc::gLockWinMenuItem->SetStatus( fInUse || fPinned);        
  855.     gGopherTextDoc= this; // track front gopher doc/window
  856.     DRichTextDoc::Activate();
  857. }
  858.  
  859. void DGopherTextDoc::Deactivate()
  860. {    
  861. #if 0
  862.     if (DGopherListDoc::gLockWinMenuItem)  
  863.         DGopherListDoc::gLockWinMenuItem->SetStatus(false);
  864.     if (gGopherTextDoc == this) gGopherTextDoc= NULL;  
  865. #endif
  866.     DRichTextDoc::Deactivate();
  867. }
  868.  
  869.  
  870. Boolean DGopherTextDoc::IsMyTask(DTask* theTask) 
  871. {
  872.     if (theTask->fKind == kGoTextdoc) {
  873.         ProcessTask( theTask);
  874.         return true;
  875.         }
  876.     else 
  877.         return DWindow::IsMyTask(theTask);
  878. }
  879.  
  880. void DGopherTextDoc::ProcessTask( DTask* theTask)
  881. {
  882.     if (theTask->fNumber == cStuf || theTask->fNumber == cStufInline) { 
  883.         fStore->ProcessTask( theTask);
  884.         if (!fStore->fRichHandler) this->ShowMessage("done reading");
  885.         }
  886.     else
  887.         DRichTextDoc::ProcessTask( theTask);
  888. }
  889.  
  890.  
  891. void DGopherTextDoc::PinWindow(Boolean turnon)
  892.     fPinned= turnon;
  893.     if (fStickpin) {
  894.         if (fPinned) fStickpin->SetIcon( &gPushpinIn, true);
  895.         else fStickpin->SetIcon( &gPushpinOut, true);
  896.         }
  897. }
  898.  
  899. Boolean DGopherTextDoc::IsMyAction(DTaskMaster* action) 
  900. {
  901.     switch(action->Id()) {
  902.     
  903.         case cParentDrop:
  904.             return true;
  905.  
  906.         case cPushpin:
  907.             PinWindow( !fPinned);
  908.             return true;
  909.  
  910.         default: 
  911.             return DRichTextDoc::IsMyAction(action);
  912.         }
  913. }
  914.  
  915.  
  916. const char* DGopherTextDoc::GetFileToSave(DFile*& aFile)
  917. {
  918.     static char *name;
  919.     enum   { kLineMax = 512 };
  920.     char     filename[kLineMax];
  921.     char   *suffix = NULL, *macsire = NULL, *mactype = NULL;
  922.     Boolean    useTemp;
  923.     char     *writeflag= "w"; 
  924.     
  925.     useTemp= (fStore->fDocFormat != DRichHandler::kTextformat 
  926.                 && (fStore->fDocFile || fStore->fFetchFile));
  927.     if (!aFile) {
  928.         name= this->GetTitle();
  929.         if (useTemp) switch (fStore->fDocFormat) {
  930.             case DRTFHandler::kRTFformat  : 
  931.                 DRTFHandler::RTFFileSigs( suffix, mactype, macsire);
  932.                 writeflag= "w"; 
  933.                 break;
  934.             case DPICTHandler::kPICTformat:  
  935.                 DPICTHandler::PICTFileSigs( suffix, mactype, macsire); 
  936.                 writeflag= "wb"; 
  937.                 break;
  938.             case DGIFHandler::kGIFformat    : 
  939.                 DGIFHandler::GIFFileSigs( suffix, mactype, macsire); 
  940.                 writeflag= "wb"; 
  941.                 break;
  942.             case DHTMLHandler::kHTMLformat: 
  943.                 DHTMLHandler::HTMLFileSigs( suffix, mactype, macsire); 
  944.                 writeflag= "w"; 
  945.                 break;
  946.             default:
  947.                 DRichHandler::RichFileSigs( suffix, mactype, macsire); 
  948.                 writeflag= "w"; 
  949.                 break;
  950.             }
  951.         else  
  952.             DRichHandler::RichFileSigs( suffix, mactype, macsire); 
  953.         sprintf(filename, "%s%s", name, suffix);
  954.         MemFree(name);
  955.         name= (char*) gFileManager->GetOutputFileName(filename);  
  956.         if (name) aFile= new DFile( name, writeflag, mactype, macsire);
  957.         }
  958.     else  {
  959.         name= (char*) aFile->GetName();
  960.         }
  961.     return name;
  962. }
  963.  
  964.  
  965. void DGopherTextDoc::Save(DFile* aFile)
  966. {
  967.     char    *name = NULL, *data, *readkind, *writekind;
  968.     char     *mackind= "TEXT", *macsire= "ttxt";
  969.     Boolean callerOwnsFile= (aFile != NULL);
  970.     Boolean    useTemp;
  971.     
  972.     useTemp= (fStore->fDocFormat != DRichHandler::kTextformat 
  973.                 && (fStore->fDocFile || fStore->fFetchFile));
  974.     gCursor->watch();
  975.     if (callerOwnsFile) 
  976.         name= (char*) aFile->GetName();
  977.     else        
  978.         name= (char*) GetFileToSave( aFile);
  979.         
  980.     if (aFile) {
  981.         if (useTemp) { 
  982.             mackind= aFile->fType;
  983.             macsire= aFile->fSire;
  984.             aFile->Delete();
  985.             }
  986.         else {
  987.             fRichView->Save(aFile); 
  988.             this->NotDirty(); // !? is this correct, but only for current fRichView !?
  989.             }
  990.         if (!callerOwnsFile) delete aFile; //aFile->suicide();
  991.         }
  992.  
  993.     switch( fStore->fDocFormat ) {
  994.         case DPICTHandler::kPICTformat:  
  995.         case DGIFHandler::kGIFformat:  
  996.             writekind= "wb";
  997.             readkind= "rb";
  998.             break;
  999.                 
  1000.         case DHTMLHandler::kHTMLformat:  
  1001.         case DRTFHandler::kRTFformat:
  1002.         case DRichHandler::kTextformat:
  1003.         default: 
  1004.             writekind= "w";
  1005.             readkind= "r";
  1006.             break;
  1007.         }
  1008.             
  1009.     if (useTemp) {
  1010.         DFile tFile( name, writekind, mackind, macsire);
  1011.         fStore->WriteDoc( &tFile, readkind, writekind);
  1012.         this->NotDirty(); // !? is this correct, but only for current fRichView !?
  1013.         }
  1014.             
  1015.     gCursor->arrow();
  1016. }
  1017.  
  1018.  
  1019. void DGopherTextDoc::ShowMessage(const char* msg)
  1020. {
  1021.     if (fStatus) fStatus->SetTitle( (char*)msg);
  1022. }
  1023.  
  1024.  
  1025.  
  1026. void DGopherTextDoc::Open()
  1027. {
  1028.     if (!fStore->OpenStore()) 
  1029.         this->suicide();
  1030.     else {
  1031.                 // add status line after DRichView...
  1032.                 // this is for Window part of doc
  1033.         Nlm_ResizeWindow( (Nlm_GraphiC)fWindow, kWinDragHeight, kWinScrollWidth,
  1034.                              kWinMinWidth, kWinExtraHeight+16);
  1035.         MakeSmallFont();
  1036.         Nlm_PoinT nps;
  1037.         this->GetNextPosition( &nps);
  1038.         DPrompt* pr= new DPrompt( 0, this, "Status:", 0, 0, gSmallFont); // Nlm_programFont
  1039.         pr->SetResize( DView::fixed, DView::moveinsuper);
  1040.         // positioning is messed up, maybe because this is at bottom of window??
  1041.         //this->NextSubviewToRight();
  1042.         nps.x += Nlm_StringWidth( "Status:");
  1043.         this->SetNextPosition(nps);
  1044.         
  1045.         fStatus= new DPrompt(  0, this, NULL, 350, 0, gSmallFont); // Nlm_programFont
  1046.         fStatus->SetResize( DView::fixed, DView::moveinsuper);
  1047.         
  1048.         if (fStatus) {
  1049.             fStore->fGolist->SetStatus( fStatus);
  1050.             if (fStore->fParentGo) fStore->fParentGo->fStatusLine= fStatus;
  1051.             }
  1052.         
  1053.         this->SetTitle( (char*)fStore->GetTitle());
  1054.         fSaveHandler= this; // replace fRichView's handler w/ us    
  1055.         fPrintHandler= fRichView; // make sure current view is printed
  1056.         fRichView->SizeToSuperview( this, true, false); // this needs to adjust for if view has scrollbar !
  1057.         this->ShowMessage("Reading...");
  1058.         gGopherTextDoc= this; // track front gopher doc/window
  1059.         
  1060.         // DRichTextDoc::Open() does ResizeWindow, SizeToSuperview && DWIndow::Open 
  1061.         // ?? use it instead ??
  1062.         DWindow::Open();
  1063.         }
  1064. }
  1065.  
  1066.  
  1067.  
  1068.  
  1069.  
  1070.  
  1071.  
  1072.  
  1073.  
  1074.  
  1075.  
  1076.  
  1077.  
  1078.  
  1079.  
  1080.  
  1081.  
  1082.  
  1083.  
  1084.  
  1085. //class DGoLinkedTextDoc : public DGopherTextDoc
  1086.  
  1087. Boolean DGoLinkedTextDoc::gAutoloadInlines = TRUE;
  1088.  
  1089. DGoLinkedTextDoc::DGoLinkedTextDoc(long id, DGopher* itsGopher, DGopherList* itsList) :
  1090.         DGopherTextDoc( id, itsGopher),
  1091.         fGopherDoc(NULL),
  1092.         fInlineParts(NULL)
  1093. #if 1
  1094.     if (itsList) itsList->newOwner();
  1095.     else itsList= new DGopherList(NULL);
  1096.     fStore->fIsLinkDoc= true;
  1097.     fStore->SetGolist(itsList);
  1098. #else
  1099.     fGopherDoc= itsGoDoc;  
  1100.     if (!fGopherDoc) {
  1101.         fGopherDoc= new DGopherListDoc();
  1102.         fGopherDoc->fGolist= new DGopherList(NULL);
  1103.         fGopherDoc->fGoview= new DGolistView(0, fGopherDoc, fGopherDoc->fGolist, kGolistViewWidth);
  1104.         }
  1105.     else
  1106.         fGopherDoc->newOwner();
  1107.     fStore->fIsLinkDoc= true;
  1108.     fStore->SetGolist(fGopherDoc->fGolist);
  1109. #endif
  1110. }
  1111.         
  1112. DGoLinkedTextDoc::~DGoLinkedTextDoc()
  1113. {
  1114.     if (fGopherDoc) fGopherDoc->suicide(); //???
  1115.     if (fInlineParts) delete fInlineParts;
  1116. }
  1117.  
  1118.  
  1119. Boolean DGoLinkedTextDoc::PushNewDoc( DGopher* itsGopher, DGopherList* itsList, Boolean havelinks) 
  1120. {
  1121.     if (itsList) itsList->newOwner();
  1122.     else itsList= new DGopherList(NULL);
  1123.  
  1124.     if (!DGopherTextDoc::PushNewDoc( itsGopher, itsList, true)) 
  1125.         return false;
  1126.  
  1127.     fStore->fIsLinkDoc= true;
  1128.     fStore->SetGolist(itsList);
  1129.     
  1130.     return true;
  1131. }
  1132.  
  1133. Boolean DGoLinkedTextDoc::PushNewLocalDoc(DFile* docFile, DFile* goFile)
  1134. {
  1135.     if (!this->PushNewDoc( (DGopher*)NULL, (DGopherList*)NULL, true)) 
  1136.         return false;
  1137.     if ( ReadFrom( docFile, goFile) ) {
  1138.         fStore->OpenStore();
  1139.         if (fStore->fParentGo) this->SetTitle((char*)fStore->GetTitle());
  1140.         else if (docFile) this->SetTitle((char*)docFile->GetName());
  1141.         }
  1142.     else 
  1143.         this->PopDoc(1);
  1144.     return true;
  1145. }
  1146.  
  1147.  
  1148. // static
  1149. short DGoLinkedTextDoc::TestFormat(char* databuf, ulong datasize, char*& datastart) 
  1150. {
  1151.     short format = 0;
  1152.     datastart= NULL;
  1153.     DRichHandler* doctest;
  1154.     long        ioffs;
  1155.     
  1156.     if ( Nlm_StrNICmp( databuf, "+MENU:", 6 ) == 0) {
  1157.         // this is a shaky test, need better magic key for this data ?!!?
  1158.         datastart= databuf;
  1159.         return DGopher::cGopherNetDoc;
  1160.         }
  1161.     else {
  1162.         char* cp= (char*) MemChr( databuf, '\n', MIN(datasize,255));
  1163.       if (cp && Nlm_StrNICmp( cp+1, "+MENU:", 6 ) == 0)  {
  1164.             datastart= cp+1;
  1165.             return DGopher::cGopherNetDoc;
  1166.             }
  1167.         }
  1168.         
  1169.     if ( DGopherList::IsGopherLine( databuf) ) {
  1170.         datastart= databuf;
  1171.         return DGopher::cNewGopherFolder; // == 2211
  1172.         }
  1173.  
  1174.     doctest = new DRTFHandler( NULL, NULL);
  1175.     datastart= doctest->IsRich( databuf, datasize);
  1176.     if (datastart) format= doctest->Format();
  1177.     delete doctest;
  1178.     if (format) return format;
  1179.         
  1180.     doctest = new DPICTHandler( NULL, NULL);
  1181.     datastart= doctest->IsRich( databuf, datasize);
  1182.     if (datastart)  format= doctest->Format();
  1183.     delete doctest;
  1184.     if (format) return format;
  1185.  
  1186.     doctest = new DGIFHandler( NULL, NULL);
  1187.     datastart= doctest->IsRich( databuf, datasize);
  1188.     if (datastart)  format= doctest->Format();
  1189.     delete doctest;
  1190.     if (format) return format;
  1191.  
  1192.     doctest = new DHTMLHandler( NULL, NULL);
  1193.     datastart= doctest->IsRich( databuf, datasize);
  1194.     if (datastart)  format= doctest->Format();
  1195.     delete doctest;
  1196.     if (format) return format;
  1197.  
  1198.     doctest = new DRichHandler( NULL, NULL);
  1199.     datastart= databuf;
  1200.     format= doctest->Format();
  1201.     delete doctest;
  1202.     return format;
  1203. }
  1204.  
  1205.  
  1206. // ?????
  1207. void DGoLinkedTextDoc::ProcessTask( DTask* theTask)
  1208. {
  1209.     if (theTask->fNumber == cProcessInlines) {
  1210.         if (!fStore->fStufGo) { // !! we can't do more than one at a time !! (not yet)
  1211.             if (!ReadInlinePart() )
  1212.                 theTask->SetRepeat(false);  // dequeue this task
  1213.             }
  1214.         }
  1215.     else 
  1216.         DGopherTextDoc::ProcessTask( theTask); 
  1217.  
  1218.     if (theTask->fNumber == cStuf) {
  1219.         if (!fStore->fStufGo 
  1220.          || fStore->fStufGo->ThreadProgress() == DGopher::kThreadDoneReading) {
  1221.             // add fGolist links into fRichView text.... (do also for net-read doc)
  1222.             this->ShowMessage("marking links...");
  1223.             this->AddGolinksToDoc();
  1224.             fRichView->Invalidate();
  1225.             this->ShowMessage("ready");
  1226.             fStore->fStufGo= NULL;
  1227.             }
  1228.         }
  1229.     else if (theTask->fNumber == cStufInline) {
  1230.         if (!fStore->fStufGo 
  1231.          || fStore->fStufGo->ThreadProgress() == DGopher::kThreadDoneReading) {
  1232.             fRichView->Invalidate();
  1233.             this->ShowMessage("ready");
  1234.             fStore->fStufGo= NULL;
  1235.             }
  1236.         }
  1237. }
  1238.  
  1239.  
  1240. class DGoStyle : public DObject
  1241. {
  1242. public:
  1243.     DGopher*         fGo;
  1244.     DRichStyle* fStyle;
  1245.     short    fStyleindex;
  1246.     
  1247.     DGoStyle( short indx, DRichStyle* st, DGopher* go):
  1248.         fStyleindex(indx), fGo(go), fStyle(st)
  1249.     {
  1250.     }
  1251.     ~DGoStyle() {}
  1252. };
  1253.  
  1254.  
  1255. Boolean DGoLinkedTextDoc::ReadInlinePart()
  1256. {
  1257.     DGoStyle* gs = (DGoStyle*) fInlineParts->Dequeue();
  1258.     fStore->fStufGo= NULL;
  1259.     if (gs) {
  1260.         //fStore->fStufGo= gs->fGo;
  1261.         //fStore->fStufGo->ReadTask();  // tell gopher to read data thru a repeat task
  1262.         gs->fGo->ReadTask();
  1263.         if (gs->fGo->fThreadState != DGopher::kThreadDoneReading) { 
  1264. #if 1
  1265.             fStore->StartInlineFetch( gs->fGo, gs->fStyle);
  1266. #else
  1267.             if (!fStore->fDocFile && fStore->fFetchFile) {
  1268.                 fStore->fDocFile= fStore->fFetchFile;
  1269.                 fStore->fFetchFile= new DTempFile();
  1270.                 }
  1271.             fStore->fFirststuff= 1;
  1272.             fStore->fInlineStyle= gs->fStyle;
  1273. #endif
  1274.             DTask* aStuffTask= newTask( cStufInline, kGoTextdoc);
  1275.             aStuffTask->SetRepeat(true);
  1276.             this->PostTask( aStuffTask); 
  1277.             }
  1278.         else 
  1279.             fStore->fStufGo= NULL;
  1280.         delete gs;
  1281.         return true;
  1282.         }
  1283.     else 
  1284.         return false;
  1285. }
  1286.  
  1287.  
  1288. void DGoLinkedTextDoc::SetupInlinePart( DRichStyle* st)
  1289. {
  1290.     Boolean added= false;
  1291.     Boolean needtask= (!fInlineParts || fInlineParts->GetSize() == 0);
  1292.     if (st->ispict && st->fPict && st->fPict->HasNetPict()) {
  1293.         short linkid= st->fPict->fLinks[0].fLinkid;
  1294.         if (linkid) {
  1295.             DGopher* ag= fStore->fGolist->GopherAt(linkid-1);
  1296.             if (ag) {
  1297.                 DGoStyle* gs= new DGoStyle( 0, st, ag);
  1298.                 if (!fInlineParts) fInlineParts= new DList();
  1299.                 fInlineParts->Queue( gs);
  1300.                 added= true;
  1301.                 }
  1302.             }
  1303.         }
  1304.     if (needtask && added && fInlineParts->GetSize()) {
  1305.         DTask* aStuffTask= newTask( cProcessInlines, kGoTextdoc);
  1306.         aStuffTask->SetRepeat(true);
  1307.         this->PostTask( aStuffTask); 
  1308.         }
  1309. }
  1310.  
  1311.  
  1312. void DGoLinkedTextDoc::SetupInlineParts()
  1313. {
  1314.     Boolean added= false;
  1315.     Boolean needtask= (!fInlineParts || fInlineParts->GetSize() == 0);
  1316.     short i, n= fRichView->fStyles->GetSize();
  1317.     for (i= 0; i<n; i++) {
  1318.         DRichStyle* st= (DRichStyle*) fRichView->fStyles->At(i);
  1319.         if (st->ispict && st->fPict && st->fPict->HasNetPict()) {
  1320.             short linkid= st->fPict->fLinks[0].fLinkid;
  1321.             if (linkid) {
  1322.                 DGopher* ag= fStore->fGolist->GopherAt(linkid-1);
  1323.                 if (ag) {
  1324.                     DGoStyle* gs= new DGoStyle( i, st, ag);
  1325.                     if (!fInlineParts) fInlineParts= new DList();
  1326.                     fInlineParts->Queue( gs);
  1327.                     added= true;
  1328.                     }
  1329.                 }
  1330.             }
  1331.         }
  1332.     if (needtask && added && fInlineParts->GetSize()) {
  1333.         DTask* aStuffTask= newTask( cProcessInlines, kGoTextdoc);
  1334.         aStuffTask->SetRepeat(true);
  1335.         this->PostTask( aStuffTask); 
  1336.         }
  1337. }
  1338.  
  1339.  
  1340.  
  1341.  
  1342. void DGoLinkedTextDoc::ExtractGopherDoc()
  1343. {
  1344.     fStore->fGolist->newOwner();
  1345.     if (fStore->fParentGo) fStore->fParentGo->newOwner();
  1346.     fStore->fGolist->fParentMenu= fStore->fParentGo;
  1347. #if 0
  1348.         // Replace is buggy still 
  1349.     if (gGopherDoc && gGopherDoc->ReplaceData( fStore->fGolist)) 
  1350.         ;  // never here because this doc is active & gGopherDoc is NULL if not frontwin
  1351.     else
  1352. #endif
  1353.   {
  1354.         DGopherListDoc* win= 
  1355.           new DGopherListDoc( DGopherListDoc::kGoListdoc, fStore->fGolist, 
  1356.                                                 this->GetTitle());
  1357.         //win->Open(true); //<< new does this !?
  1358.         }
  1359. }
  1360.  
  1361.  
  1362. void DGoLinkedTextDoc::OpenGopherLink( short gopherItem, Nlm_PoinT* mappt)
  1363.     if (fStore->fGolist) {
  1364.         DGopher* ag= fStore->fGolist->GopherAt(gopherItem);
  1365.         OpenGopherLink( ag, mappt);
  1366.         }
  1367. }
  1368.  
  1369. void DGoLinkedTextDoc::OpenGopherLink( DGopher* ag, Nlm_PoinT* mappt)
  1370.     if (ag) {   
  1371.         char *maps = NULL;
  1372.         gCursor->watch();
  1373.         this->ShowMessage("opening link...");
  1374.         Boolean nooption= false;
  1375.         Boolean optionIsOn= gKeys->option();
  1376.         if (ag) {
  1377.             nooption= (ag->fType == kTypeFolder 
  1378.                 || ag->fType == kTypeQuery  // ??
  1379.                 || ag->fIsMap
  1380.                 || ag->fIsPlus != kGopherPlusYes); 
  1381.             if (!nooption && DGopherListDoc::gOptionIsOn) 
  1382.                 optionIsOn= !optionIsOn;
  1383.             if (mappt) {
  1384.                 char  buf[128];
  1385.                 sprintf( buf, "\t%d,%d", mappt->x, mappt->y);
  1386.                 maps= StrDup( buf);
  1387.                 ag->fQuery= maps;
  1388.                 }
  1389.             if (optionIsOn)  
  1390.                 (void) DGopherListDoc::GopherByViewDialog(ag, ag->fOwnerList);
  1391.             else {
  1392.                 short itsViewchoice= DGopherListDoc::DefaultGopherView(ag);
  1393.                 DGopherListDoc::ProcessGopher( ag, itsViewchoice);
  1394.                 }
  1395.             if (mappt && maps) {
  1396.                     // !? maker of fQuery must free it when gopher processing is done!?
  1397.                     // can we fix DGopher::CloseQuery() to handle this?
  1398.                     // e.g., all fQuery must be owned by the gopher item...
  1399.                 maps= (char*) MemFree(maps);
  1400.                 }
  1401.              }
  1402.         gCursor->arrow();
  1403.          }
  1404. }
  1405.  
  1406.  
  1407. Boolean DGoLinkedTextDoc::IsMyAction(DTaskMaster* action) 
  1408. {
  1409.     switch(action->Id()) {
  1410.     
  1411.         case cParentDrop:
  1412.             return true;
  1413.         
  1414.         case cShiftFore:
  1415.             this->PopDoc(-1);
  1416.             return true;
  1417.             
  1418.         case cShiftBack:
  1419.             this->PopDoc(1);
  1420.             return true;
  1421.             
  1422.         case cMenuPopup: {
  1423.             DPopupList* pl= (DPopupList*) action;  
  1424.             if (pl) OpenGopherLink( pl->GetValue() - 1);
  1425.             }
  1426.             return true;
  1427.  
  1428.         default: 
  1429.             return DGopherTextDoc::IsMyAction(action);
  1430.         }
  1431. }
  1432.  
  1433.  
  1434.  
  1435. Boolean DGoLinkedTextDoc::ReadFrom(DFile* docFile, DFile* goFile)
  1436. {
  1437.     if (docFile) {
  1438. #if 1
  1439.  
  1440. #else
  1441.         if (!fGopherDoc) {
  1442.             fGopherDoc= new DGopherListDoc();
  1443.             fGopherDoc->fGolist= new DGopherList(NULL);
  1444.             fGopherDoc->fGoview= new DGolistView(0, fGopherDoc, fGopherDoc->fGolist, kGolistViewWidth);
  1445.             }
  1446.         fStore->SetGolist(fGopherDoc->fGolist);
  1447. #endif
  1448.  
  1449.         if (fStatus) fStore->fGolist->SetStatus( fStatus);
  1450.         if (!fStore->fGolist->fParentMenu) {
  1451.             char docurl[1024];
  1452.             char * name = (char*) docFile->GetName();
  1453.             char quote= '\'';
  1454.             if (StrChr( name, quote)) quote= '"';
  1455.             StrCpy(docurl, " file:///");
  1456.             StrNCat(docurl, name, 1000);
  1457.             StrNCat(docurl, " ", 1000); 
  1458.             docurl[1023]= 0;
  1459.             long doclen= StrLen(docurl);
  1460.             docurl[0]= quote; // so parseURL doesn't eat spaces
  1461.             docurl[doclen-1]= quote;
  1462.             fStore->fGolist->fParentMenu= DGopherList::GopherFromURL(docurl, doclen, fStore->fGolist);
  1463.             //this->SetTitle((char*)docFile->GetName());
  1464.             // ! GopherFromURL() sticks new gopher into list also !
  1465.             }
  1466.         fStore->fParentGo= fStore->fGolist->fParentMenu;
  1467.     
  1468.       if (goFile) {
  1469.             ulong filelen;
  1470.             goFile->OpenFile();
  1471.             goFile->GetDataLength(filelen);
  1472.             char* buf= (char*) MemNew( filelen+1);
  1473.             goFile->ReadData( buf, filelen);
  1474.             //fGopherDoc->AddData( buf, filelen);
  1475.             fStore->fGolist->ReadLinks( buf, filelen);
  1476.             MemFree( buf);
  1477.             goFile->CloseFile();
  1478.             delete goFile;
  1479.             //fGopherDoc->UpdateListSize();
  1480.             //fGopherDoc->fGoview->SetColWidths(); //?
  1481.             }
  1482.             
  1483.         // delete docFile; //<< !?!?!?
  1484.         fStore->fIsFromFile= false;
  1485.         return true;
  1486.         }
  1487.     else
  1488.         return false;
  1489. }
  1490.  
  1491.  
  1492. void DGoLinkedTextDoc::Save(DFile* aFile)
  1493. {
  1494.     // we have to dup this part from DGopherTextDoc::Save() or rewrite all of save usage...
  1495.     char    filename[512];
  1496.     char    *name = NULL, *data;
  1497.     ulong    dlen;
  1498.     Boolean callerOwnsFile= (aFile != NULL);
  1499.     
  1500.     if (callerOwnsFile)  
  1501.         name= (char*) aFile->GetName();
  1502.     else        
  1503.         name= (char*) GetFileToSave( aFile);
  1504.         
  1505.     if (aFile) {
  1506.         DGopherTextDoc::Save(aFile);
  1507.         // and save data to filename.go4 == filename.DGopherListDoc::kGopherDocSuffix
  1508.  
  1509.         switch ( fStore->fDocFormat && fStore->fGolist->GetSize()>0 ) {
  1510.             case DHTMLHandler::kHTMLformat:  
  1511.                 break;
  1512.                 
  1513.             case DGIFHandler::kGIFformat:  
  1514.             case DPICTHandler::kPICTformat:  
  1515.             case DRTFHandler::kRTFformat:
  1516.             default: 
  1517.                 gCursor->watch();
  1518.                 aFile->Close();
  1519.                 StrNCpy( filename, (char*) aFile->GetName(), 512);
  1520.                 gFileManager->ReplaceSuffix(filename, 512, DGopherListDoc::kGopherDocSuffix);
  1521.             
  1522.                 data= fStore->fGolist->WriteLinks();
  1523.                 dlen= StrLen(data);
  1524.                  {
  1525.                      // sunCC is getting confused w/ DFile bFile() unless wrapped in braces !
  1526.                      DFile bFile( (const char*)filename, (const char*)"w", 
  1527.                                           (const char*)"TEXT",  (const char*)"IGo4");
  1528.                   //aFile->Initialize( filename, "w", "TEXT", "IGo4");
  1529.                   bFile.OpenFile();  
  1530.                   bFile.WriteData(data, dlen); 
  1531.                   bFile.CloseFile(); 
  1532.                  }
  1533.                 MemFree(data);
  1534.                 gCursor->arrow();
  1535.                 break;
  1536.             }
  1537.             
  1538.         if (!callerOwnsFile) delete aFile; // aFile->suicide();  
  1539.         }
  1540.     
  1541. }
  1542.  
  1543.  
  1544.  
  1545.  
  1546.  
  1547. void DGoLinkedTextDoc::InsertLink( short golistItem, DGopher* theGo, short atparag, short atchar, short atlen)
  1548. {
  1549.     DRichStyle linkStyle;
  1550.     
  1551.     DRichStyle* aStyle= fRichView->GetStyleAtChar( atparag, atchar);
  1552.     if (aStyle) linkStyle= *aStyle; 
  1553.     linkStyle.color= gLinkcolor;
  1554.     linkStyle.last = false;
  1555.     linkStyle.linkid= golistItem + 1; 
  1556.     if (theGo) {
  1557.         if (theGo->fIsMap) {
  1558.             linkStyle.ismap= true;
  1559.             fStore->fMapLink= linkStyle.linkid;
  1560.             }
  1561.         }
  1562.     fRichView->InsertStyle( atparag, atchar, atlen, &linkStyle);
  1563.     linkStyle.ownpict= false;
  1564. }
  1565.  
  1566. void DGoLinkedTextDoc::InsertLink( short golistItem, DGopher* theGo, 
  1567.                     Nlm_RecT arect, short atparag, short atchar)
  1568. {    
  1569.     DRichStyle* aStyle= fRichView->GetStyleAtChar( atparag, atchar);
  1570.     if (aStyle && aStyle->ispict && aStyle->fPict) {
  1571.              //!! arect must be relative to pict top,left !!
  1572.         if (theGo) {
  1573.             if (theGo->fIsMap) {
  1574.                 aStyle->ismap= true;
  1575.                 // fStore->fMapLink= golistItem+1;
  1576.                 }
  1577.             }
  1578.         aStyle->fPict->AddLink( DPictStyle::kPictNetLink, golistItem+1, arect);
  1579.         }
  1580. }
  1581.  
  1582. void DGoLinkedTextDoc::InsertLink( DGopher* theGo, short atparag, short atchar, short atlen)
  1583. {
  1584.     if (theGo) {
  1585.         short igo= fStore->fGolist->GetIdentityItemNo( (DObject*) theGo);
  1586.         if (igo == kEmptyIndex) {
  1587.             fStore->fGolist->InsertLast( theGo);
  1588.             igo= fStore->fGolist->GetSize()-1;
  1589.             }
  1590.       InsertLink( igo, theGo, atparag, atchar, atlen);
  1591.       }
  1592. }
  1593.  
  1594. void DGoLinkedTextDoc::InsertLink( DGopher* theGo, Nlm_RecT arect, short atparag, short atchar)
  1595. {
  1596.     if (theGo) {
  1597.         short igo= fStore->fGolist->GetIdentityItemNo( (DObject*) theGo);
  1598.         if (igo == kEmptyIndex) {
  1599.             fStore->fGolist->InsertLast( theGo);
  1600.             igo= fStore->fGolist->GetSize()-1;
  1601.             }
  1602.       InsertLink( igo, theGo, arect, atparag, atchar);
  1603.       }
  1604. }
  1605.  
  1606.  
  1607. void DGoLinkedTextDoc::LinkToSelection( DGopher* aGopher)
  1608. {
  1609.     short              selparag, selchar, selendparag, selendchar;
  1610.     Boolean         inpict, isSelected;
  1611.     Nlm_RecT        selrect;
  1612.     DRichStyle    selstyle;
  1613.     
  1614.     isSelected= fRichView->GetSelection( inpict, selparag, selchar, selendparag, selendchar, 
  1615.                                                     selrect, &selstyle);
  1616.     if (isSelected && aGopher) {
  1617.         aGopher= (DGopher*) aGopher->Clone(); 
  1618.         // must add +MENULINE or +MENURECT info to aGopher !!
  1619.         if (!aGopher->fDocLink) aGopher->fDocLink= new DocLink();
  1620.         if (inpict) {
  1621.             aGopher->fDocLink->SetLineRect( selrect, selparag, selchar);
  1622.             this->InsertLink( aGopher, selrect, selparag, selchar);
  1623.             }
  1624.         else {
  1625.             aGopher->fDocLink->SetLine( selparag, selchar, selendparag, selendchar);
  1626.             this->InsertLink( aGopher, selparag, selchar, selendchar-selchar);
  1627.             }
  1628.         fRichView->Invalidate(); // !? show new link
  1629.         Dirty();
  1630.         }
  1631.     selstyle.ownpict= false;
  1632. }
  1633.  
  1634.  
  1635. void DGoLinkedTextDoc::MarkURLs()
  1636. {
  1637.     if (fRichView && fStore->fGolist) {
  1638.         Boolean found;
  1639.         short        atparag, atchar, atlen, startparag = 0, startchar = 0;
  1640.         do {
  1641.             found= fRichView->FindURL( atparag, atchar, atlen, startparag, startchar);
  1642.             if (found) {
  1643.                 DGopher* go;
  1644.                 
  1645.                 char* itemtxt= fRichView->GetText(atparag, -1);
  1646.                 if (itemtxt) {
  1647.                   go= DGopherList::GopherFromURL( itemtxt+atchar, atlen, fStore->fGolist); 
  1648.                     InsertLink( go, atparag, atchar, atlen);
  1649.                     }
  1650.  
  1651.                 startchar= atchar+atlen+1; 
  1652.                 startparag= atparag;
  1653.                 }
  1654.         } while (found);
  1655.         }    
  1656. }
  1657.  
  1658.  
  1659. void DGoLinkedTextDoc::AddGolinksToDoc()
  1660. {
  1661.     if (gAutoloadInlines) this->SetupInlineParts();
  1662.     if (fStore->fDocFormat == DHTMLHandler::kHTMLformat) return;
  1663.  
  1664.     this->MarkURLs();
  1665.     
  1666.     if (fRichView && fStore->fGolist) {
  1667.         short igo, ngo= fStore->fGolist->GetSize();
  1668.         for (igo= 0; igo<ngo; igo++) {
  1669.             Boolean found;
  1670.             short        atparag, atchar, atlen, startparag = 0, startchar = 0;
  1671.             DGopher* go= fStore->fGolist->GopherAt(igo);
  1672.  
  1673.             if (go->fIsMap) {
  1674.                 fStore->fMapLink= igo+1;
  1675.                 }
  1676.                 
  1677.             if (go->HasDocLink()) 
  1678.                 switch (go->fDocLink->Kind()) {
  1679.                                 
  1680.                 case DocLink::kNoLink:
  1681.                     break;
  1682.                     
  1683.                 case DocLink::kLine: {
  1684.                     short endparag, endchar;
  1685.                     go->fDocLink->GetLine( atparag, atchar, endparag, endchar);
  1686.                     //InsertLink( igo, atparag, atchar, endparag, endchar);
  1687.                     InsertLink( igo, go, atparag, atchar, endchar-atchar); // need new Ins type
  1688.                     }
  1689.                     break;
  1690.  
  1691.                 case DocLink::kLineRect: {
  1692.                     Nlm_RecT rect;
  1693.                     go->fDocLink->GetLineRect( rect, atparag, atchar);
  1694.                     InsertLink( igo, go, rect, atparag, atchar);
  1695.                     }
  1696.                     break;
  1697.  
  1698.                 case DocLink::kRect: {
  1699.                     Nlm_RecT rect;
  1700.                     go->fDocLink->GetRect( rect);
  1701.                     InsertLink( igo, go, rect, 0, 0); // assume this is PICT w/ only 1 char !?
  1702.                     }
  1703.                     break;
  1704.                     
  1705.                 case DocLink::kString: {
  1706.                     short skipval = 0;
  1707.                     char* finds= go->fDocLink->GetString( skipval);
  1708.                     Boolean haveskipval= skipval>0;
  1709.                     atlen= MIN( 10240, StrLen(finds));
  1710.                     do {
  1711.                         found= fRichView->Find( finds, atparag, atchar, startparag, startchar);
  1712.                         if (skipval) skipval--;
  1713.                         if (found && !skipval) {
  1714.                             InsertLink( igo, go, atparag, atchar, atlen);
  1715.                             startchar= atchar+atlen+1; 
  1716.                             startparag= atparag;
  1717.                             if (haveskipval) found= false; // do no more
  1718.                             }
  1719.                         } while (found);
  1720.                     }
  1721.                 break;
  1722.                 
  1723.                 }
  1724.             }
  1725.         }
  1726. }
  1727.  
  1728.  
  1729.  
  1730. void DGoLinkedTextDoc::Open(DFile* docFile, DFile* goFile)
  1731. {
  1732.     if ( ReadFrom( docFile, goFile) ) 
  1733.         this->Open();
  1734.     else 
  1735.         this->Close(); //this->suicide();
  1736. }
  1737.  
  1738.  
  1739. void DGoLinkedTextDoc::Open()
  1740. {
  1741.     DGopherTextDoc::Open();
  1742.  
  1743. #if 0 
  1744.     this->NextSubviewBelowLeft();
  1745.     DPrompt* pr= new DPrompt( 0, this, "Related topics:", 0, 0, Nlm_programFont);             
  1746.     pr->SetResize( DView::fixed, DView::moveinsuper);
  1747.     this->NextSubviewToRight();
  1748.     fMenuList= new DPopupList( cMenuPopup, this);
  1749.     fMenuList->SetResize( DView::fixed, DView::moveinsuper);
  1750.  
  1751.     if (fGolist) {
  1752.         short i, n= fGolist->GetSize();
  1753.         for (i= 0; i<n; i++) {
  1754.             DGopher* go= fGolist->GopherAt(i);
  1755.             fMenuList->AddItem((char*)go->GetName()); //<< need to MenuCleanName !! ShortName());  
  1756.             }
  1757.         }
  1758. #endif
  1759. }
  1760.  
  1761.  
  1762.  
  1763.  
  1764.  
  1765.  
  1766. //class DGopherInfoDoc : public DGopherTextDoc
  1767.  
  1768. void DGopherInfoDoc::ProcessTask(DTask* theTask) 
  1769. {
  1770.     if (theTask->fNumber == cInfo) {
  1771.  
  1772.         if (fStore->fStufGo && fStore->fStufGo->ThreadProgress() != DGopher::kThreadDoneReading)  
  1773.                 fStore->fStufGo->ShowProgress();
  1774.                 
  1775.         if (!fStore->fStufGo || fStore->fStufGo->ThreadProgress() == DGopher::kThreadDoneReading) {
  1776.             theTask->SetRepeat(false); // done w/ gopher fetch, dequeue this task
  1777.             this->ShowMessage("done reading");
  1778.             if (fStore->fStufGo && fStore->fStufGo->fInfoSize) {
  1779.                 fRichView->Append( fStore->fStufGo->fInfo, NULL, NULL, 0, gTextFont);
  1780.                 fRichView->Invalidate();
  1781.                 fStore->fStufGo->DeleteInfo(); 
  1782.                 fStore->fStufGo->fTransferType= fSavedType;
  1783.                 }
  1784.             }
  1785.         }
  1786.     else {
  1787.         DWindow::ProcessTask(theTask);
  1788.         }
  1789. }
  1790.  
  1791.  
  1792. void DGopherInfoDoc::Open()
  1793. {
  1794.     fSavedType= fStore->fParentGo->fTransferType;
  1795.     fStore->fParentGo->fTransferType= kTransferText;
  1796.     fStore->fParentGo->InfoTask();   
  1797.     if (fStore->fParentGo->fThreadState == DGopher::kThreadDoneReading) { 
  1798.         this->suicide(); //delete this; 
  1799.         return; 
  1800.         }
  1801.     fStore->fStufGo= fStore->fParentGo;
  1802.     DTask* aTask= newTask(cInfo, kGoTextdoc);
  1803.     aTask->SetRepeat(true);
  1804.     this->PostTask( aTask);  
  1805.  
  1806.     //// view
  1807.     
  1808.     char title[512];
  1809.     sprintf(title,"Info for %s",(char*)fStore->GetTitle());
  1810.     this->SetTitle(title);
  1811.             // this is for Window part of doc
  1812.     Nlm_ResizeWindow( (Nlm_GraphiC)fWindow, kWinDragHeight, kWinScrollWidth,
  1813.                          kWinMinWidth, kWinExtraHeight+16);
  1814.  
  1815.     fSaveHandler= this; // replace fRichView's handler w/ us
  1816.     this->ShowMessage("Reading...");
  1817.     gGopherTextDoc= this; // track front gopher doc/window
  1818.     DWindow::Open();
  1819. }
  1820.  
  1821.  
  1822.  
  1823. //class    DGopherFileDoc : public DWindow
  1824.  
  1825.  
  1826. DGopherFileDoc::DGopherFileDoc(long id, DGopher* itsGopher) :
  1827.         DWindow( id, gApplication, fixed),
  1828.         fParentGo(itsGopher), 
  1829.         fFile(NULL), fLaunch(NULL), fMapper(NULL),
  1830.         fBytesAvail(NULL), fBytesGot(NULL),
  1831.         fTimeSpent(NULL), fBytesPerSec(NULL),
  1832.         fFirststuff(1)
  1833.     // FailNIL(fParentGo);
  1834.     fParentGo->newOwner();
  1835. }
  1836.         
  1837. DGopherFileDoc::~DGopherFileDoc()
  1838. {
  1839. #if 0
  1840.         /// nnoooooooo ... dview::deletesubviews does this
  1841.     if (fBytesAvail) delete fBytesAvail;
  1842.     if (fBytesGot) delete fBytesGot;
  1843.     if (fTimeSpent) delete fTimeSpent;
  1844.     if (fBytesPerSec) delete fBytesPerSec;
  1845. #endif
  1846.     if (fFile) delete fFile;
  1847.     delete fParentGo; //fParentGo->suicide();
  1848. }
  1849.     
  1850. Boolean DGopherFileDoc::suicide(void) 
  1851.     if (GetOwnerCount() <= 1) { 
  1852.         delete this; 
  1853.         return true; 
  1854.         }
  1855.     else 
  1856.         return DObject::suicide();
  1857. }
  1858.  
  1859. Boolean DGopherFileDoc::suicide(short ownercount)
  1860. {
  1861.     if (ownercount < 1) {
  1862.         delete this; 
  1863.         return true; 
  1864.         }
  1865.     else 
  1866.         return DObject::suicide(ownercount);
  1867. }
  1868.  
  1869.  
  1870. Boolean DGopherFileDoc::IsMyTask(DTask* theTask) 
  1871. {
  1872.     if (theTask->fKind == kGoFiledoc) {
  1873.         ProcessTask( theTask);
  1874.         return true;
  1875.         }
  1876.     else 
  1877.         return DWindow::IsMyTask(theTask);
  1878. }
  1879.  
  1880. Boolean DGopherFileDoc::IsMyAction(DTaskMaster* action) 
  1881. {
  1882.     switch (action->Id()) {
  1883.     
  1884.         case kLaunchBut:
  1885.             {
  1886.             // launch file
  1887.             char* name= (char*) fFile->GetName();
  1888. #ifdef WIN_MAC
  1889. #if 1
  1890.             DExternalHandler* child= new DExternalHandler( name);
  1891.             if (!child->Launch()) 
  1892. #else
  1893.             if (!Dgg_LaunchFile( name))
  1894. #endif
  1895.           Nlm_Message(MSG_OK, "Application launch failed");
  1896. #else
  1897.             char  buf[1024];
  1898.             char* commandline= (char*)fMapper->GetHandlerName(); // may well include $file variable
  1899.             char* cp= StrStr(commandline, "$file"); // may look for other vars later, like $url, ...
  1900.             if (cp) {
  1901.                 long len= cp - commandline;
  1902.                 StrNCpy(buf, commandline, len);
  1903.                 buf[len]= 0;
  1904.                 StrNCat(buf, name, 512);
  1905.                 StrNCat(buf, cp+5, 512);
  1906.                 }
  1907.             else {
  1908.                 StrNCpy(buf, commandline, 512);
  1909.                 StrNCat(buf, " ",512);
  1910.                 StrNCat(buf,name,512);
  1911.                 }                
  1912. #if 1
  1913.             DExternalHandler* child= new DExternalHandler( buf);
  1914.             if (!child->Launch()) 
  1915. #else
  1916.             if (!Dgg_LaunchFile( buf))
  1917. #endif
  1918.            Nlm_Message(MSG_OK, "Application launch failed");
  1919. #endif
  1920.             }
  1921.             return true;
  1922.             
  1923.         default: 
  1924.             return DWindow::IsMyAction(action);        
  1925.         }
  1926. }
  1927.             
  1928.  
  1929. void DGopherFileDoc::ProcessTask(DTask* theTask) 
  1930. {
  1931.     if (theTask->fNumber == cSave) {
  1932.         char snum[128];
  1933.         Boolean alldone =  (fParentGo->ThreadProgress() == DGopher::kThreadDoneReading);
  1934.  
  1935.         if (fParentGo->fInfoSize) {
  1936.             char * cbeg = fParentGo->fInfo;
  1937.             ulong clen = fParentGo->fInfoSize;
  1938.             short err= fFile->WriteData( cbeg, clen);
  1939.             fParentGo->DeleteInfo(); 
  1940.             //fParentGo->fInfo= (char*)MemGet(1, true); // NOW part of DGopher::DeleteInfo()
  1941.             
  1942.             // also, update progress stats on display
  1943.             Dgg_LongToStr(fParentGo->fBytesReceived, snum, 0, 128);
  1944.             fBytesGot->SetTitle( snum);
  1945.             Dgg_LongToStr(fParentGo->fConnectTime, snum, 0, 128);
  1946.             fTimeSpent->SetTitle( snum);
  1947.  
  1948.             long csec = fParentGo->fConnectTime; // time / 60;
  1949.             if (csec > 0) {
  1950.                 csec= fParentGo->fBytesReceived / csec;
  1951.                 Dgg_LongToStr( csec, snum, 0, 128);
  1952.                 fBytesPerSec->SetTitle( snum);
  1953.                 }
  1954.  
  1955.             if (fFirststuff>0) {
  1956.                 long bytesavail;
  1957.                 if (fParentGo->fBytesExpected > 0) bytesavail= fParentGo->fBytesExpected; 
  1958.                 else bytesavail= fParentGo->fSizeLong;
  1959.                 Dgg_LongToStr(bytesavail, snum, 0, 128);
  1960.                 fBytesAvail->SetTitle( snum);
  1961.                 fFirststuff= 0;
  1962.                 }
  1963.             }
  1964.         
  1965.         if (alldone) {
  1966.             char sdone[80];
  1967.             StrCpy(sdone, "Done ");
  1968.             Dgg_LongToStr(fParentGo->fConnectTime, snum, 0, 20);
  1969.             StrCat(sdone, snum);
  1970.             fTimeSpent->SetTitle( sdone);
  1971.             if (fLaunch) {
  1972.                 fLaunch->Enable();
  1973.                 //fLaunch->Invalidate(); //?? do we need this to get it redrawn?
  1974.                 }
  1975.                 
  1976.             theTask->SetRepeat(false); // done w/ gopher fetch, dequeue this task
  1977.             fFile->CloseFile();
  1978.             }
  1979.         }
  1980.         
  1981.     else {
  1982.         DWindow::ProcessTask(theTask);
  1983.         }
  1984. }
  1985.  
  1986.  
  1987. void DGopherFileDoc::Open()
  1988. {
  1989.     char namebuf[256], *name;
  1990.     long  macType, macSire;
  1991.     char *suffix, macTypeStr[6], macSireStr[6];
  1992.     
  1993.     StrCpy(namebuf, (char*) fParentGo->ShortName());
  1994.     name= namebuf;
  1995.     this->SetTitle(name);
  1996.  
  1997.     // TESTING....
  1998.     //Nlm_ResizeWindow( (Nlm_GraphiC)fWindow, kWinDragHeight, kWinScrollWidth,
  1999.     //                     kWinMinWidth, kWinExtraHeight);
  2000.     
  2001.     // ?? is this info already set in fParentGo ?? ->fLaunch, ->fMacType, ->fMacSire
  2002.     // YES -- call to ItemForm before this doc is created sets it in fParentGo !
  2003.     fMapper= gGopherMap->GetPreferedFiletype(fParentGo, macType, macSire, suffix);
  2004.     if (fMapper) {
  2005. #ifdef WIN_MAC
  2006.         StrCpy(macTypeStr, Idtype2Str(macType));
  2007.         StrCpy(macSireStr, Idtype2Str(macSire));
  2008. #else
  2009.         gFileManager->ReplaceSuffix( namebuf, 255, suffix);
  2010. #endif
  2011.         MemFree(suffix);
  2012.         }
  2013.     
  2014.     name= (char*)gFileManager->GetOutputFileName( namebuf);
  2015.     if (!name) {
  2016.         this->suicide(); //delete this;
  2017.         return;
  2018.         }
  2019.     
  2020.     DFile* itsFile= new DFile( name, "wb", macTypeStr, macSireStr); //!! NEED "b" or stupid ansi/NLM converts newlines !
  2021.     if (!itsFile) { this->suicide(); return; } //delete this // ?? is this a reasonable abort?...
  2022.     fFile= itsFile;
  2023.     fFile->OpenFile();
  2024.     
  2025.     // add (open)/cancel button
  2026.     // static text w/ file name, total size, & size transferred
  2027.     // progress indicator ??
  2028.  
  2029.     DView *super = this;
  2030.     DPrompt *pr, *prDate = NULL;
  2031.     DCluster* clus;
  2032.     
  2033.     pr= new DPrompt( 0, super, "File:", 0, 0, Nlm_programFont);             
  2034.     super->NextSubviewToRight();
  2035.     pr= new DPrompt( 0, super, (char*)gFileManager->FilenameFromPath( name), 0, 0, Nlm_programFont);             
  2036.     super->NextSubviewBelowLeft();
  2037.  
  2038.             // left side
  2039.     clus= new DCluster(0, this, 0, 0, true);
  2040.     super = clus;
  2041.     if (fParentGo->fDate) {
  2042.         pr= new DPrompt( 0, super, "File Date:", 0, 0, Nlm_programFont);             
  2043.         super->NextSubviewBelowLeft();
  2044.         }
  2045.     pr= new DPrompt( 0, super, "Total bytes:", 0, 0, Nlm_programFont);             
  2046.     super->NextSubviewBelowLeft();
  2047.     pr= new DPrompt( 0, super, "Received:", 0, 0, Nlm_programFont);             
  2048.     super->NextSubviewBelowLeft();
  2049.     pr= new DPrompt( 0, super, "Time:", 0, 0, Nlm_programFont);             
  2050.     super->NextSubviewBelowLeft();
  2051.     pr= new DPrompt( 0, super, "Bytes/sec:", 0, 0, Nlm_programFont);             
  2052.     super->NextSubviewBelowLeft();
  2053.  
  2054.             // right side
  2055.     char *kFillnum = "     000000";
  2056.     this->NextSubviewToRight();
  2057.     clus= new DCluster(0, this, 0, 0, true);
  2058.     super = clus;
  2059.     if (fParentGo->fDate) {
  2060.         prDate= new DPrompt( 0, super, kFillnum, 0, 0, Nlm_programFont, justright);             
  2061.         prDate->SetTitle( fParentGo->fDate);
  2062.         super->NextSubviewBelowLeft();
  2063.         }    
  2064.     fBytesAvail=     new DPrompt( 0, super, kFillnum, 0, 0, Nlm_programFont, justright);             
  2065.     super->NextSubviewBelowLeft();
  2066.     fBytesGot=         new DPrompt( 0, super, kFillnum, 0, 0, Nlm_programFont, justright);             
  2067.     super->NextSubviewBelowLeft();    
  2068.     fTimeSpent=     new DPrompt( 0, super, kFillnum, 0, 0, Nlm_programFont, justright);             
  2069.     super->NextSubviewBelowLeft();    
  2070.     fBytesPerSec= new DPrompt( 0, super, kFillnum, 0, 0, Nlm_programFont, justright);             
  2071.     super->NextSubviewBelowLeft();
  2072.     
  2073.     if (fMapper && (fMapper->fHandlerType == 2 || fMapper->fHandlerName)) {   // 2 == kExternalHandler !! fix this??
  2074.         fLaunch= new DButton( kLaunchBut, super, "Launch");
  2075.         fLaunch->Disable();
  2076.         }
  2077.     
  2078.     //fParentGo->fTransferType= kTransferBinary; // ?? is this messed up ??
  2079.     if (fParentGo->fThreadState == DGopher::kThreadNotStarted) 
  2080.         fParentGo->ReadTask();   
  2081.     if (fParentGo->fThreadState == DGopher::kThreadDoneReading) { 
  2082.         fParentGo->fThreadState= DGopher::kThreadNotStarted; // ! need to reset this somewhere for used gophers
  2083.         this->suicide(); //delete this; 
  2084.         return; 
  2085.         }
  2086.     //fParentGo= fParentGo;
  2087.     DTask* aSaveTask= newTask(cSave, kGoFiledoc);
  2088.     aSaveTask->SetRepeat(true);
  2089.     this->PostTask( aSaveTask); // ?? bug in gTaskCentral, getting only ReadTask in queue...
  2090.  
  2091.     DWindow::Open();
  2092. }
  2093.  
  2094.  
  2095.  
  2096.  
  2097.  
  2098.  
  2099.  
  2100.  
  2101.  
  2102.  
  2103.